package com.nali.dal.mongo.explain.operate;

import java.util.List;

import com.nali.dal.exception.SyntaxException;
import com.nali.dal.expression.ExpressionValue;
import com.nali.dal.expression.PropertyExpression;
import com.nali.dal.expression.operate.Modifier;
import com.nali.dal.expression.query.Criteria;
import com.nali.dal.expression.query.CriteriaType;
import com.nali.dal.mongo.explain.query.Condition;

public class Update {
	private Condition condition = new Condition();
	private Operate operate = new Operate();
	
	public void addPropertyModifier(PropertyExpression<Modifier> pc) {
		operate.add(pc.getExpression(), pc.getPropertyName());
	}

	public void addModifierValue(ExpressionValue<Modifier> mv) {
		PropertyExpression<Modifier> pc = mv.getPropertyExpression();
		operate.add(pc.getExpression(), pc.getPropertyName(), mv.getValue());
	}
	
	public void addPropertyCriteria(PropertyExpression<Criteria> pc) {
		CriteriaType type = pc.getExpression().type;
		switch (type) {
		case where:
			addWhere(pc);
			break;
		case nativeExpression:
			addNativeExpression(pc);
			break;
		default:
			throw new SyntaxException("not supported type:" + type);
		}
	}
	public void addCriteriaValue(ExpressionValue<Criteria> cv) {
		CriteriaType type = cv.getPropertyExpression().getExpression().type;
		switch (type) {
		case where:
			addWhere(cv);
			break;
		default:
			throw new SyntaxException("not supported type:" + type);
		}
	}

	private void addNativeExpression(PropertyExpression<Criteria> pc) {
		switch (pc.getExpression()) {
		case where:
			condition.addNativeWhere(pc.getPropertyName());
			break;
		default:
			throw new SyntaxException("not supported criteria:" + pc.getExpression());
		}
	}
	
	private void addWhere(PropertyExpression<Criteria> cv) {
		condition.add(cv.getExpression(), cv.getPropertyName());
	}
	
	private void addWhere(ExpressionValue<Criteria> cv) {
		PropertyExpression<Criteria> pc = cv.getPropertyExpression();
		condition.add(pc.getExpression(), pc.getPropertyName(), cv.getValue());
	}

	public PreparedUpdate prepare() {
		condition.check();
		return new PreparedUpdate(condition, operate);
	}
	
	public static PreparedUpdate parse(List<ExpressionValue<Criteria>> expressionValues,
			List<PropertyExpression<Criteria>> propertyExpressions, List<ExpressionValue<Modifier>> modifiers) {
		Update update = new Update();
		for (ExpressionValue<Modifier> mv : modifiers) {
			update.addModifierValue(mv);
		}
		for (ExpressionValue<Criteria> expressionValue : expressionValues) {
			update.addCriteriaValue(expressionValue);
		}
		for (PropertyExpression<Criteria> propertyExpression : propertyExpressions) {
			update.addPropertyCriteria(propertyExpression);
		}
		return update.prepare();
	}
}
